'use strict';
const nunjucks = require('nunjucks');
const assert = require('assert');
const util = require('util');
const extend = require('extend');
const debug = require('debug')('koa-grace:view');
const error = require('debug')('koa-grace-error:view');
/**
 * Loop Helper function
 *
 */
function whileLoop(obj, fn) {
    var keys = Object.keys(obj);
    var i = keys.length;
    while (i--) {
        fn(keys[i], obj[keys[i]]);
    }
}
/**
 * Adds a custom filter, globals and extension
 *
 * @param  env  The Nunjucks Environment
 * @param  obj The KoaNunjucks settings that includes filters,globals and extensions
 * @reference https://mozilla.github.io/nunjucks/api.html
 */
const addCustom = async (env, obj) => {
    try {
        const addC = Object.keys(obj).map(i => {
                if (i == 'filters') {
            whileLoop(obj.filters, (name, fn) => {
                env.addFilter(name, fn);
        });
        } else if (i == 'globals') {
            whileLoop(obj.globals, (name, fn) => {
                env.addGlobal(name, fn);
        });
        } else if (i == 'extensions') {
            whileLoop(obj.extensions, (name, fn) => {
                env.addExtension(name, fn);
        });
        }
    });
        await Promise.all(addC);
    } catch (e) {
        console.warn('Nunjucks Error', e);
    }
};

/**
 * Add `render` method.
 * @param {String} path (view的默认文件夹) 
 * @param {String} site (默认网站的配置) 
 * @param {Object} options (optional)
 *        {Object} options.nunjucks nunjucks template config.
 *        {Object} options.filters nunjucks filter function
 *        {Object} options.globals nunjucks globals function
 *        {Object} options.extensions nunjucks globals function
 *        {String} options.suffix nunjucks view name suffix,default is '.html'
 * @return {Function}
 *
 * @todo 添加测试用例
 */

module.exports = function KoaNunjucks(path,site,options) {
    
    if (!path) {
		throw new Error('default_view_root config required.');
    }
    if(!options || !options.nunjucks) {
        Object.assign(options,{nunjucks:{}});
        Object.assign(options.nunjucks,{
            autoescape: true,
            throwOnUndefined: false,
            trimBlocks: false,
            lstripBlocks: false,
            watch: false,
            noCache: false            
        });
    }
    if(options.filters){
        Object.assign(options,{filters: {}});
    }
    if(options.globals){
        Object.assign(options,{globals: {}});
    }
    if(options.extensions){
        Object.assign(options,{extensions: {}});
    }
    if(options.suffix){
        Object.assign(options,{suffix:'.html'});
    }	
    const env = nunjucks.configure(path, options.nunjucks);
    env.render = util.promisify(env.render);
    env.renderString = util.promisify(env.renderString);
    addCustom(env, options);

    return async function(ctx, next) {
        if (ctx.render) return await next();
        
        if(!ctx.state.site){
            Object.assign(ctx.state,{site:site});
        }

        Object.assign(ctx, {
            render : async function (view, context){
                try {
                    context = Object.assign({}, ctx.state, context);
                    view += options.suffix;
                    const body = await env.render(view, context);
                    ctx.type = ctx.type || 'text/html';
                    ctx.body = body;
                    return;
                } catch (e) {
                    console.warn('Nunjucks Render Error:', e);
                }
            },
            renderString : async function(string, context) {
                try {
                    assert(typeof string === 'string');
                    context = Object.assign({}, ctx.state, context);
                    return await env.renderString(string, context);
                } catch (e) {
                    console.warn('Nunjucks RenderString Error:', e);
                }
            }
        });
        await next();
    };

}